﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Quality
{
    class ProcessCababilityHelper
    {

        /// <summary>
        /// 直方图分组确定
        /// </summary>
        /// <param name="length">样本数</param>
        /// <returns>返回分组数</returns>
        public static int GetGroupNum(int length)
        {
            double k = 1 + 3.31 * Math.Log10(length);
            return (int)Math.Floor(k);
        }
        /// <summary>
        /// 获取子组容量
        /// </summary>
        /// <param name="length">样本数</param>
        /// <param name="groupNum">子组数</param>
        /// <returns>返回子组容量</returns>
        public static int GetGroupSize(int length, int groupNum)
        {
            return (int)Math.Ceiling((decimal)length / (decimal)groupNum);
        }
        /// <summary>
        /// 获取望目值
        /// </summary>
        /// <param name="usl">目标上限值</param>
        /// <param name="lsl">目标下限值</param>
        /// <returns>返回望目值</returns>
        public static double GetTarget(double usl, double lsl)
        {
            return DoubleFormat(5, (usl + lsl) / 2);
        }
        // <summary>
        /// 获取分辨率，如果返回resolution为0，则标识数组中数据均相同
        /// </summary>
        /// <param name="original">原始数据</param>
        /// <returns>返回数据分辨率</returns>
        public static double GetResolution(double[] original)
        {
            try
            {
                Array.Sort(original);
                if (original.Length == 1)
                {
                    return DoubleFormat(5, original[0]);
                }
                double resolution = 0.0;
                for (int i = 1; i < original.Length; i++)
                {
                    resolution = original[i] - original[i - 1];
                    if (resolution > 0)
                    {
                        break;
                    }
                }
                return DoubleFormat(5, resolution);
            }
            catch (Exception ex)
            {
                return 0.0;
            }
        }
        /// <summary>
        /// 组距，数据有误或数据均相同时返回0
        /// </summary>
        /// <param name="digits">精度，小数位数</param>
        /// <param name="max">最大值</param>
        /// <param name="min">最小值</param>
        /// <param name="groupNum">子组数</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回组距</returns>
        public static double GetGroupDistance(int digits, double max, double min, int groupNum, double[] original)
        {
            double gd = 0.0;
            if (GetResolution(original) != 0)
            {
                double r = GetResolution(original);
                double originalDistance = Math.Abs(max - min) / groupNum;
                gd = DoubleFormat(digits, Math.Ceiling(originalDistance / r) * r);
            }
            return gd;
        }
        // <summary>
        /// 获取组内左边界
        /// </summary>
        /// <param name="min">最小值</param>
        /// <param name="groupDistance">组距</param>
        /// <param name="groupNum">子组数</param>
        /// <returns>返回组内左边界</returns>
        public static double[] GetGroupDownBound(double min, double groupDistance, int groupNum)
        {
            double[] gdb = new double[groupNum];
            for (int i = 1; i <= groupNum; i++)
            {
                gdb[i - 1] = DoubleFormat(5, min + (i - 1) * groupDistance - groupDistance / 2);
            }
            return gdb;
        }
        /// <summary>
        /// 获取组内右边界
        /// </summary>
        /// <param name="min">最小值</param>
        /// <param name="groupDistance">组距</param>
        /// <param name="groupNum">子组数</param>
        /// <returns>返回组内右边界</returns>
        public static double[] GetGroupUpBound(double min, double groupDistance, int groupNum)
        {
            double[] gub = new double[groupNum];
            for (int i = 1; i <= groupNum; i++)
            {
                gub[i - 1] = DoubleFormat(5, min + (i - 1) * groupDistance + groupDistance / 2);
            }
            return gub;
        }
        /// <summary>
        /// 获取组内中值
        /// </summary>
        /// <param name="min">最小值</param>
        /// <param name="groupDistance">组距</param>
        /// <param name="groupNum">子组数</param>
        /// <returns>返回组内中值</returns>
        public static double[] GetGroupMedian(double min, double groupDistance, int groupNum)
        {
            double[] gm = new double[groupNum];
            for (int i = 1; i <= groupNum; i++)
            {
                gm[i - 1] = DoubleFormat(5, min + (i - 1) * groupDistance);
            }
            return gm;
        }
        /// <summary>
        /// 分布密度
        /// </summary>
        /// <param name="groupDistance">组距</param>
        /// <param name="groupDownBound">组内左边界</param>
        /// <param name="groupUpBound">组内右边界</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回分布密度</returns>
        public static double[] GetDistributionDensity(double groupDistance, double[] groupDownBound, double[] groupUpBound, double[] original)
        {
            double[] g = new double[groupUpBound.Length];
            double[] count = new double[groupUpBound.Length];
            //初始化count数值
            for (int i = 0; i < count.Length; i++)
            {
                count[i] = 0.0;
            }
            for (int i = 0; i < groupUpBound.Length; i++)
            {
                for (int j = 0; j < original.Length; j++)
                {
                    if (groupDownBound[i] < original[j] && original[j] < groupUpBound[i])
                    {
                        count[i] = count[i] + 1;
                    }
                }
            }

            for (int i = 0; i < count.Length; i++)
            {
                g[i] = DoubleFormat(5, (count[i] / original.Length) * (1 / groupDistance));
            }
            return g;
        }
        /// <summary>
        /// 获取正态分布数据密度
        /// </summary>
        /// <param name="original">原始数据</param>
        /// <returns>返回分布密度</returns>
        public static double[] GetNormalDistribution(double[] original)
        {
            double[] values = new double[original.Length];
            for (int i = 0; i < original.Length; i++)
            {
                values[i] = DoubleFormat(5, 1 / ((Math.Sqrt(2 * Math.PI) * SixSigmaHelper.GetTotalStdDev(original))) * Math.Pow(Math.E, -Math.Pow((original[i] - SixSigmaHelper.GetAverageValue(original)), 2) / (2 * Math.Pow(SixSigmaHelper.GetTotalStdDev(original), 2))));
            }
            return values;
        }
        /// <summary>
        /// 获取单个正态分布数据
        /// </summary>
        /// <param name="x"></param>
        /// <param name="ave">均值</param>
        /// <param name="stdDev">标准差</param>
        /// <returns>返回单个正态分布数据</returns>
        public static double GetNormalDistributionValue(double x, double ave, double stdDev)
        {
            return DoubleFormat(5, 1 / ((Math.Sqrt(2 * Math.PI) * stdDev)) * Math.Pow(Math.E, -Math.Pow((x - ave), 2) / (2 * Math.Pow(stdDev, 2))));
        }
        /// <summary>
        /// 获取组内正态分布数据密度
        /// </summary>
        /// <param name="groupNum">子组数</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回组内正态分布数据密度</returns>
        public static double[] GetGroupNormalDistribution(int groupNum, double[] original)
        {
            double[] values = new double[original.Length];
            double inGroupStdDev = SixSigmaHelper.GetInGroupStdDev(2, original);
            double ave = SixSigmaHelper.GetAverageValue(original);
            for (int i = 0; i < original.Length; i++)
            {
                values[i] = DoubleFormat(5, 1 / ((Math.Sqrt(2 * Math.PI) * inGroupStdDev) * Math.Pow(Math.E, -Math.Pow((original[i] - ave), 2) / (2 * Math.Pow(inGroupStdDev, 2)))));
            }
            return values;
        }
        /// <summary>
        /// 获取准确度
        /// </summary>
        /// <param name="usl">目标上限</param>
        /// <param name="lsl">目标下限</param>
        /// <param name="ave">均值</param>
        /// <returns>返回准确度</returns>
        public static double GetCA(double usl, double lsl, double ave)
        {
            double diff = (usl - lsl) / 2;
            double sum = (usl + lsl) / 2;
            return DoubleFormat(5, Math.Abs(sum - ave) / diff);
        }
        /// <summary>
        /// ppm小于lsl，ppm百万分之不良
        /// </summary>
        /// <param name="lsl">目标下限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回ppm小于lsl的值</returns>
        public static double GetPpmLessLsl(double lsl, double[] original)
        {
            double count = 0.0;
            for (int i = 0; i < original.Length; i++)
            {
                if (original[i] < lsl)
                {
                    count += 1;
                }
            }
            return DoubleFormat(5, (count / original.Length) * 1000000);
        }
        /// <summary>
        /// ppm大于usl，ppm百万分之不良
        /// </summary>
        /// <param name="usl">目标上限</param>
        /// <param name="original">原始数据</param>
        /// <returns>返回ppm大于usl的值</returns>
        public static double GetPpmMoreUsl(double usl, double[] original)
        {
            double count = 0.0;
            for (int i = 0; i < original.Length; i++)
            {
                if (original[i] > usl)
                {
                    count += 1;
                }
            }
            return DoubleFormat(5, (count / original.Length) * 1000000);
        }
        /// <summary>
        /// 获取正三倍标准差
        /// </summary>
        /// <param name="expTarget">目标值</param>
        /// <param name="stdDev">标准差</param>
        /// <returns>返回目标值加上3倍的标准差</returns>
        public static double GetPositive3Signa(double expTarget, double stdDev)
        {
            return DoubleFormat(5, expTarget + 3 * stdDev);
        }
        /// <summary>
        /// 获取负三倍标准差
        /// </summary>
        /// <param name="expTarget">目标值</param>
        /// <param name="stdDev">标准差</param>
        /// <returns>返回目标值减去3倍的标准差</returns>
        public static double GetNegative3Signa(double expTarget, double stdDev)
        {
            return DoubleFormat(5, expTarget - 3 * stdDev);
        }

        /// <summary>
        /// 按指定精度进行数据格式化
        /// </summary>
        /// <param name="digits">精度，小数位数</param>
        /// <param name="d">要格式化的数据</param>
        /// <returns>返回格式化后的数据</returns>
        public static double DoubleFormat(int digits, double d)
        {
            double result = 0.0;
            if (double.TryParse(d.ToString(), out result))
            {
                decimal b = new decimal(d);
                double formatD = Convert.ToDouble(Math.Round(b, digits));
                //Double formatD = b.setScale(digits, BigDecimal.ROUND_HALF_UP).doubleValue();
                return formatD;
            }
            else
            {
                return 0.0;
            }
        }
    }
}
